package fdog;

/**
 *
 * @author Eduardo Freire
 */
public class LineIntegralConvolution {
    public static final int X = 0;
    public static final int Y = 1;

    private int L = 20;
    private double ds = 0.5;
    private double vectorField[][][];
    private int[][] pixels;
    private int width;
    private int height;
    private double min = Double.MAX_VALUE;
    private double max = -Double.MAX_VALUE;

    

    public LineIntegralConvolution(int[][] pixels, int width, int height, double[][][] vectorField) throws Exception {
        this.vectorField = vectorField;
        this.width = width;
        this.height = height;
        this.pixels = pixels;
    }

    public double[][] getFlow() throws Exception {
        double pixelsOut[][] = new double[width][height];
        double conv;

        for(int i = 0; i < width; i++) {
            for(int j = 0; j < height; j++) {
                conv = convolution(i, j);

                if(conv < min) min = conv;
                else if(conv > max) max = conv;

                pixelsOut[i][j] = conv;
            }
        }

        return pixelsOut;
    }

    private double convolution(int x0, int y0) {
        double[] v1 = vectorField[x0][y0];
        double[] v2 = v1;
        double x1 = x0;
        double y1 = y0;
        double x2 = x0;
        double y2 = y0;
        double sum = getPixelValue(x0, y0);

        for(int s = 0; s < L; s++) {
            x1 = x1 + ds*v1[X];
            y1 = y1 + ds*v1[Y];
            x2 = x2 - ds*v2[X];
            y2 = y2 - ds*v2[Y];

            if(x1 >= 0 && y1 >= 0 && x1 < width && y1 < height) {
                v1 = getVector(x1, y1, v1);
            }
            if(x2 >= 0 && y2 >= 0 && x2 < width && y2 < height) {
                v2 = getVector(x2, y2, v2);
            }

            sum += getPixelValue(x1, y1);
            sum += getPixelValue(x2, y2);
        }

        return sum / (2*L + 1);
    }

    private double[] getVector(double x, double y, double[] v) {
        double[] v1 = vectorField[(int)x][(int)y];

        if(v[X]*v1[X] + v[Y]*v1[Y] < 0) {
            v1[X] = -v1[X];
            v1[Y] = -v1[Y];
        }
        
        return v1;
    }

    private double getPixelValue(double x, double y) {
        x = Math.abs(x) % width;
        y = Math.abs(y) % height;

        int i = (int)x;
        int j = (int)y;
        int i1 = (i + 1) % width;
        int j1 = (j + 1) % height;

        double f_iy = pixels[i][j] + (y - j)*(pixels[i][j1] - pixels[i][j]);
        double f_i1y = pixels[i1][j] + (y - j)*(pixels[i1][j1] - pixels[i1][j]);

        return f_iy + (x - i)*(f_i1y - f_iy);
    }

    public double getMax() {
        return max;
    }

    public double getMin() {
        return min;
    }
}
